﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Multiprocessing.Core.Algorithm;
using Multiprocessing.Core.Mathematics.Matrices;
using Multiprocessing.Core.Units;

namespace Multiprocessing.Algorithms.Matrices
{
    public class SumMatricesAlgorithm : BaseAlgorithm<Matrix>
    {
        #region Constructors

        public SumMatricesAlgorithm(IProcessingBlockFactory processingBlockFactory, string name) : base(processingBlockFactory, name)
        {
        }

        public SumMatricesAlgorithm(string name) : base(name)
        {
        }

        #endregion

        public override object[] CreateParameters(int size)
        {
            Matrix first = Matrix.Factory.CreateRandom<Matrix>(size, size),
                   second = Matrix.Factory.CreateRandom<Matrix>(size, size);
            return new object[] {first, second};
        }

        public override AlgorithmResult<Matrix> Execute(IProcessingBlock processingBlock, params object[] args)
        {
            return this.Execute(processingBlock, (Matrix) args[0], (Matrix) args[1]);
        }

        public AlgorithmResult<Matrix> Execute(IProcessingBlock processingBlock, Matrix first, Matrix second)
        {
            if (ReferenceEquals(processingBlock, null))
            {
                throw new ArgumentNullException("processingBlock");
            }
            if (ReferenceEquals(first, null))
            {
                throw new ArgumentNullException("first");
            }
            if (ReferenceEquals(second, null))
            {
                throw new ArgumentNullException("second");
            }

            Matrix result = Matrix.Factory.Create<Matrix>(first.RowCount, first.ColumnCount);

            int rowPerUnit = result.RowCount/processingBlock.Size, rest = result.RowCount%processingBlock.Size;

            for (int i = 0; i < processingBlock.Size - 1; ++i)
            {
                int startRow = i*rowPerUnit;
                processingBlock[i] = () =>
                                         {
                                             for (int j = 0; j < rowPerUnit; ++j)
                                             {
                                                 int row = startRow + j;
                                                 for (int column = 0; column < first.ColumnCount; ++column)
                                                 {
                                                     result[row, column] = first[row, column] + second[row, column];
                                                 }
                                             }
                                         };
            }
            // last + rest;
            int lastUnitIdx = processingBlock.Size - 1;

            processingBlock[lastUnitIdx] = () =>
                                               {
                                                   int startRow = lastUnitIdx*rowPerUnit;
                                                   for (int j = 0; j < rowPerUnit + rest; ++j)
                                                   {
                                                       int row = startRow + j;
                                                       for (int column = 0; column < first.ColumnCount; ++column)
                                                       {
                                                           result[row, column] = first[row, column] +
                                                                                 second[row, column];
                                                       }
                                                   }
                                               };
            this.Stopwatch.Restart();
            processingBlock.StartAll();
            processingBlock.WaitAll();
            this.Stopwatch.Stop();
            return new AlgorithmResult<Matrix>(result, this.Stopwatch.Elapsed, -1);
        }
    }
}
